package org.lisy.java.collection;

import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import com.alibaba.fastjson.JSON;

/**
 * 集合的 Stream 操作
 */
public class StreamOperate {

	/**
	 * Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象
	 * 特性及优点：
	 *   无存储: Stream 不是一种数据结构，它只是某种数据源的一个视图，数据源可以是一个数组，Java 容器或 I/O channel 等
	 *   为函数式编程而生: 对 Stream 的任何修改都不会修改背后的数据源，比如对 Stream 执行过滤操作并不会删除被过滤的元素，而是会产生一个不包含被过滤元素的新 Stream  
	 *   惰式执行: Stream 上的操作并不会立即执行，只有等到用户真正需要结果的时候才会执行
	 *   可消费性: Stream 只能被“消费”一次，一旦遍历过就会失效，就像容器的迭代器那样，想要再次遍历必须重新生成
	 */
	
	/**
	 * 流的处理，主要有三种关键性操作: 
	 *   1. 流的创建: 获取一个流，用于后续操作
	 *   2. 中间操作（ intermediate operation）: 中间操作的输入和输出都是 Stream，中 间操作可以是过滤、转换、排序等
	 *   3. 最终操作(terminal operation): 最终操作可以将 Stream 转成其他形式，如计算出流中元素的个数、将流装 换成集合、以及元素的遍历等
	 */
	
	public static void main(String[] args) {
		
		// 流的创建
		buildStream();
		
		/*---- 流的中间操作 ----*/
		
		// 筛选与切片
		filterStream();
		
		// 映射
		mapStream();
		
		// 排序
		sortStream();
		
		// 消费
		peekStream();
		
		/*---- 流的终端操作 ----*/
		
		// 遍历
		cyclicStream();
		
		// 匹配
		matchStream();
		
		// 聚合
		polymerizeStream();
		
		// 收集
		collectStream();
	}

	private static void buildStream() {
		List<String> list = Arrays.asList("a", "b", "c");
		// 用集合创建顺序流
		Stream<String> stream = list.stream();
		System.out.println("list build stream " + stream);
		
		// 用集合创建并行流
		Stream<String> parallelStream = list.parallelStream();
		System.out.println("list build parallelStream " + parallelStream);
		
		// 顺序流转并行流
		Stream<String> changeStream = stream.parallel();
		System.out.println("stream change parallelStream " + changeStream);
		
		// 用数组创建流
		int[] array={1,3,5,6,8};
		IntStream intStream = Arrays.stream(array);
		System.out.println("list build stream " + intStream);
		
		// 用Stream中的静态方法of()创建流
		Stream<Integer> ofStream = Stream.of(1,5,3,4,2,6);
		System.out.println("of() build stream " + ofStream);
		
		// 用Stream中的静态方法iterate()创建流
		Stream<Integer> iterateStream = Stream.iterate(0, (x) -> x + 2).limit(6);
		System.out.println("iterate() build stream " + iterateStream);
		
		// 用Stream中的静态方法generate()创建流
		Stream<Double> generateStream = Stream.generate(Math::random).limit(2);
		System.out.println("generate() build stream " + generateStream);
		
		// 使用 Pattern.splitAsStream() 方法，将字符串分隔成流
		Pattern pattern = Pattern.compile(",");
		Stream<String> patternStream = pattern.splitAsStream("a,b,c,d");
		System.out.println("Pattern build stream " + patternStream);
	}

	private static void filterStream() {
		List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1, 7);
        // 使用filter()过滤符合条件的元素
        list.stream().filter(x -> x > 6).forEach(System.out::print);
        System.out.println("");
        
        // 使用distinct()去重
        list.stream().distinct().forEach(System.out::print);
        System.out.println("");
        
        // 使用skip()跳过元素
        list.stream().skip(2).forEach(System.out::print);
        System.out.println("");
        
        // 使用limit()限制条数
        list.stream().limit(3).forEach(System.out::print);
        System.out.println("");
	}

	private static void mapStream() {
		List<String> list = Arrays.asList("a,b,c", "1,2,3");
		 
		// 将每个元素转成一个新的且不带逗号的元素
		Stream<String> s1 = list.stream().map(s -> s.replaceAll(",", ""));
		s1.forEach(val -> System.out.print(val + " "));
		System.out.println("");
		 
		// 将每个元素转换成一个stream，连接
		Stream<String> s3 = list.stream().flatMap(s -> {
		    String[] split = s.split(",");
		    Stream<String> s2 = Arrays.stream(split);
		    return s2;
		});
		s3.forEach(val -> System.out.print(val + " "));
		System.out.println("");
	}
	
	private static void sortStream() {
		List<String> list = Arrays.asList("aa", "ff", "dd");
		// sorted()排序，String类自身已实现Compareable接口
		list.stream().sorted().forEach(val -> System.out.print(val + " "));
		System.out.println("");
		
		// 自然序逆序元素，使用Comparator 提供的reverseOrder() 方法
		list.stream().sorted(Comparator.reverseOrder()).forEach(val -> System.out.print(val + " "));
		System.out.println("");
		
		// sorted(Comparator com)排序，自定义排序规则
		Student s1 = new Student("aa", 30);
		Student s2 = new Student("bb", 20);
		Student s3 = new Student("aa", 10);
		Student s4 = new Student("dd", 40);
		List<Student> studentList = Arrays.asList(s1, s2, s3, s4);
		studentList.stream().sorted((o1, o2) -> {
			// 先按姓名升序，姓名相同则按年龄升序
			if (o1.getName().equals(o2.getName())) {
				return o1.getAge() - o2.getAge();
			} else {
				return o1.getName().compareTo(o2.getName());
			}
		}).forEach(stu -> System.out.print(stu.toString() + " / "));
		System.out.println("");

		studentList.stream().sorted(Comparator
			.comparing(Student::getName, Comparator.reverseOrder())
			.thenComparing(Student::getAge, Comparator.naturalOrder())
		).toList().forEach(stu -> System.out.print(stu.toString() + " / "));
		System.out.println("");
	}
	
	private static void peekStream() {
		Student s1 = new Student("aa", 10);
		Student s2 = new Student("bb", 20);
		List<Student> studentList = Arrays.asList(s1, s2);
		studentList.stream().peek(o -> o.setAge(100)).forEach(stu -> System.out.print(stu.toString() + " / "));
		System.out.println("");
	}

	private static void cyclicStream() {
        List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
        // 使用forEach遍历输出元素
        list.stream().forEach(System.out::print);
        System.out.println("");
	}
	
	private static void matchStream() {
		List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
		// 使用allMatch()判断
		boolean allMatch = list.stream().allMatch(e -> e > 10);
		System.out.println("allMatch() x > 6：" + allMatch);
		
		// 使用noneMatch()判断
		boolean noneMatch = list.stream().noneMatch(e -> e > 10);
		System.out.println("noneMatch() x > 6：" + noneMatch);
		
        // 使用anyMatch()判断
        boolean anyMatch = list.stream().anyMatch(x -> x > 6);
        System.out.println("anyMatch() x > 6：" + anyMatch);
        
        // 匹配第一个findFirst()
        Optional<Integer> findFirst = list.stream().filter(x -> x > 6).findFirst();
        System.out.println("findFirst(): " + findFirst.get());
        
        // 匹配任意findAny()-适用于并行流
        Optional<Integer> findAny = list.parallelStream().filter(x -> x > 6).findAny();
        System.out.println("findAny(): " + findAny.get());
	}

	private static void polymerizeStream() {
		List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
		// 统计
		long count = list.stream().count();
		System.out.println("count(): " + count);
		
		// 获取最大值
		Integer max = list.stream().max(Integer::compareTo).get();
		System.out.println("max(): " + max);
		
		// 获取最小值
		Integer min = list.stream().min(Integer::compareTo).get();
		System.out.println("min(): " + min);
	}
	
	private static void collectStream() {
		Student s1 = new Student("cc", 10);
		Student s2 = new Student("bb", 20);
		Student s3 = new Student("aa", 10);
		List<Student> list = Arrays.asList(s1, s2, s3);
		
		// 装成list  [10, 20, 10]
		List<Integer> ageList = list.stream().map(Student::getAge).collect(Collectors.toList());
		System.out.println("toList " + JSON.toJSONString(ageList));
		
		// 转成set [20, 10]
		Set<Integer> ageSet = list.stream().map(Student::getAge).collect(Collectors.toSet());
		System.out.println("toSet " + JSON.toJSONString(ageSet));
		
		// 转成map,注:key不能相同，否则报错 {cc=10, bb=20, aa=10}
		Map<String, Integer> studentMap = list.stream().collect(Collectors.toMap(Student::getName, Student::getAge));
		System.out.println("toMap " + JSON.toJSONString(studentMap));
		// 转成 map, Function.identity() 会直接返回输入的值，key 冲突时保留现有值，指定 Map 类型
		// {"cc":{"age":10,"name":"cc"},"bb":{"age":20,"name":"bb"},"aa":{"age":10,"name":"aa"}}
		LinkedHashMap<String, Student> collectMap = list.stream().collect(Collectors.toMap(
				Student::getName, Function.identity(), (existingValue, newValue) -> existingValue, LinkedHashMap::new));
		System.out.println("toMap " + JSON.toJSONString(collectMap));

		// 字符串分隔符连接  (cc,bb,aa)
		String joinName = list.stream().map(Student::getName).collect(Collectors.joining(",", "(", ")"));
		System.out.println("toMap " + joinName);
		
		// 聚合操作
		// 1.学生总数 3
		Long count = list.stream().collect(Collectors.counting());
		System.out.println("counting " + count);
		// 2.最大年龄 (最小的minBy同理) 20
		Integer maxAge = list.stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compare)).get();
		System.out.println("maxBy " + maxAge);
		// 3.所有人的年龄 40
		Integer sumAge = list.stream().collect(Collectors.summingInt(Student::getAge));
		System.out.println("summingInt " + sumAge);
		// 4.平均年龄 13.333333333333334
		Double averageAge = list.stream().collect(Collectors.averagingDouble(Student::getAge));
		System.out.println("averagingDouble " + averageAge);
		// 带上以上所有方法
		DoubleSummaryStatistics statistics = list.stream().collect(Collectors.summarizingDouble(Student::getAge));
		System.out.println("count:" + statistics.getCount() + ",max:" + statistics.getMax() + ",min:" + statistics.getMin() + ",sum:" + statistics.getSum() + ",average:" + statistics.getAverage());
		 
		// 分组
		Map<Integer, List<Student>> ageMap = list.stream().collect(Collectors.groupingBy(Student::getAge));
		System.out.println("groupingBy " + JSON.toJSONString(ageMap));
		// 多重分组,先根据类型分再根据年龄分
		Map<Integer, Map<String, List<Student>>> typeAgeMap = list.stream().collect(Collectors.groupingBy(Student::getAge, Collectors.groupingBy(Student::getName)));
		System.out.println("groupingBy " + JSON.toJSONString(typeAgeMap));
		
		// 分区
		// 分成两部分，一部分大于10岁，一部分小于等于10岁
		Map<Boolean, List<Student>> partMap = list.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 10));
		System.out.println("partitioningBy " + JSON.toJSONString(partMap));
		
		// 规约
		Integer allAge = list.stream().map(Student::getAge).collect(Collectors.reducing(Integer::sum)).get();
		System.out.println("reducing " + allAge);
	}
}

class Student {
	
	private String name;
	
	private int age;

	public Student(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}

}
